/************************************************************************
* framebuffer.c
* voxelands - 3d voxel world sandbox game
* Copyright (C) Lisa 'darkrose' Milne 2016 <lisa@ltmnet.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
************************************************************************/

#include "common.h"
#include "graphics.h"
#include "list.h"

static struct {
	framebuffer_t *buffers;
	int ids;
} fbo_data = {
	NULL,
	0
};

/* create a texture attachment for a fbo */
static int fbo_create_texture(int w, int h, fbo_buffer_t *b, uint8_t d)
{
	texture_t *t;

	t = tex_create();

	glGenTextures(1,&t->glid);
	glBindTexture(GL_TEXTURE_2D, t->glid);
	if (d) {
		glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, w, h, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
	}else{
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
	}

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	if (d) {
		glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, t->glid, 0);
	}else{
		glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, t->glid, 0);
	}

	t->state = 1;

	b->texture = t;

	return 0;
}

/* create a buffer attachment for a fbo */
static int fbo_create_buffer(int w, int h, fbo_buffer_t *b)
{
	glGenRenderbuffers(1,&b->glid);

	glBindRenderbuffer(GL_RENDERBUFFER, b->glid);
	glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, w, h);
	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, b->glid);

	return 0;
}

/* create a frame buffer object of width and height, with colour and depth buffers optionally bound to textures */
framebuffer_t *fbo_create(int w, int h, uint8_t c_texture, uint8_t d_texture)
{
	framebuffer_t *f;

	f = malloc(sizeof(framebuffer_t));
	if (!f)
		return NULL;

	f->depth.glid = 0;
	f->colour.glid = 0;
	f->id = ++fbo_data.ids;
	f->w = w;
	f->h = h;

	glGenFramebuffers(1,&f->glid);

	glBindFramebuffer(GL_FRAMEBUFFER, f->glid);

	if (c_texture) {
		glDrawBuffer(GL_COLOR_ATTACHMENT0);
		fbo_create_texture(w,h,&f->colour,0);
	}else{
		glDrawBuffer(GL_NONE);
	}

	if (d_texture) {
		fbo_create_texture(w,h,&f->depth,1);
	}else{
		fbo_create_buffer(w,h,&f->depth);
	}

	glBindFramebuffer(GL_FRAMEBUFFER, 0);

	fbo_data.buffers = list_append(&fbo_data.buffers,f);

	return f;
}

/* free a framebuffer */
void fbo_free(framebuffer_t *f)
{
	/* TODO: this */
}

/* get a framebuffer by id */
framebuffer_t *fbo_get(int id)
{
	framebuffer_t *f = fbo_data.buffers;

	while (f) {
		if (f->id == id)
			return f;
		f = f->next;
	}

	return NULL;
}

/* use/bind a framebuffer */
void fbo_use(framebuffer_t *f)
{
	glBindTexture(GL_TEXTURE_2D, 0);
	glBindFramebuffer(GL_FRAMEBUFFER, f->glid);
	glViewport(0, 0, f->w, f->h);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glEnable(GL_DEPTH_TEST);
}

/* unbind framebuffers */
void fbo_unbind_all()
{
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
	glViewport(0, 0, wm_data.size.width, wm_data.size.height);
}
